/**
 * @author paim 01/11/2011
 */
package paim.gh.app;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.EventListener;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;

import javax.swing.event.EventListenerList;

import paim.wingchun.app.WC_Log;
import paim.wingchun.model.pojos.Erro;
import paim.wingchun.view.Dialogs;

import paim.gh.controller.eventos.ArvoreEvent;
import paim.gh.model.modelos.GradeModel;
import paim.gh.model.pojos.*;
import paim.gh.view.paineis.arvore.Arvore;
import paim.gh.view.paineis.arvore.ArvoreModel;
import paim.gh.view.paineis.toolBar.GradeToolsListener;

/**
 * @author paim 01/11/2011
 */
public final class Gradiador implements Observer, GradiadorListener, GradeToolsListener, Gerenciavel {

    private static Gradiador instancia;

    private transient boolean isProcessando;

    public synchronized final boolean isProcessando() {
        return this.isProcessando;
    }

    public synchronized final void setProcessando(boolean isProcessando) {
        this.isProcessando = isProcessando;
    }

    public enum Estado {
        STOPPED, PAUSED, RUNNING
    }

    private Estado estado = Estado.STOPPED;

    protected enum Evento {
        REPLACED, ITEM$REMOVED, ITEM$ADDED, ITEM$UPDATED
    }

    EventListenerList listenerList = new EventListenerList();

    public final Class<?>[] classesBalde = {
            Curso.class,
            Dia.class,
            Disciplina.class,
            Doscente.class,
            Horario.class,
            Sala.class,
            Semestre.class,
            Turno.class };

    private List<Erro> erros = new ArrayList<>();

    private Grade grade;

    private Gradiador() {}

    public static Gradiador getInstance() {
        if ( instancia == null )
            instancia = new Gradiador();
        return instancia;
    }

    @Override
    public void setListeners() {
        addGradiatorListener(this);
        Gerenciador.getInstancia().getGradeTools().addListener(this);
    }

    @Override
    public void update(Observable observable, Object args) {
        if ( ArvoreEvent.class.isAssignableFrom(observable.getClass()) ) {
            Enum<?> evento = (Enum<?>) ((Object[]) args)[0];

            if ( EnumSet.of(Arvore.Eventos.ADD, Arvore.Eventos.ADDALL, Arvore.Eventos.ADDTURMA, Arvore.Eventos.REMOVE,
                    Arvore.Eventos.REMOVEALL, Arvore.Eventos.REMOVETURMA).contains(evento) ) {
                stop();
            }

        }
    }

    /**
     * carrega uma grade na sessao correta e
     *
     * @author paim 07/11/2011
     * @param id
     *            void
     */
    public final void loadGrade(Long id) {
        stop();
        try {
            Grade grade = GradeModel.getInstance().get(GHApp.getSessao(), id);
            setGrade(grade);
        }
        catch ( Exception e ) {
            Dialogs.erro(e.toString(), "load");
            WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
        }
    }

    /**
     * sempre a grade principal
     *
     * @author temujin Oct 29, 2011
     * @return Grade
     */
    public final synchronized Grade getGrade() {
        return grade;
    }

    public final synchronized void setGrade(Grade grade) {
        this.grade = grade;
        fireEvent(Evento.REPLACED);
    }

    public synchronized Balde getBalde() {
        return grade.getBalde();
    }

    public List<Erro> getErros() {
        return erros;
    }

    public void setErros(List<Erro> erros) {
        this.erros = erros;
    }

    public Estado getEstado() {
        return this.estado;
    }

    @Override
    public void changeMestre() {
        // TODO Auto-generated method stub

    }

    @Override
    public void showMestre() {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean start() {
        estado = Estado.RUNNING;
        return true;
    }

    /**
     * deve interromper todo {@link ThreadGroup} de processamento e remover todas as grades alternativas ....
     *
     * @author temujin Oct 29, 2011 void
     */
    @Override
    public void stop() {
        estado = Estado.STOPPED;
        if ( grade == null )
            return;
        removeAlternativas();
    }

    @Override
    public void pause() {
        estado = Estado.PAUSED;

    }

    @Override
    public void continua() {
        estado = Estado.RUNNING;

    }

    private void removeAlternativas() {
        grade.getGradesAlternativas().clear();
        getErros().clear();
        // try {
        // List<Erro> errossalvar = saveOrUpdate();
        // getErros().addAll(errossalvar);
        // }
        // catch ( Exception e ) {
        // // TODO: handle exception
        // e.printStackTrace();
        // }
    }

    public List<Erro> saveOrUpdate() throws Exception {
        // FIXME tratar de implementar uma forma de cache aqui, para somente executar
        // a persistencia de fato quando for necessaria
        List<Erro> errossalvar = GradeModel.getInstance().saveOrUpdate(GHApp.getSessao(), getGrade());
        return errossalvar;
    }

    private ArvoreModel getTreeModel() {
        Arvore arvore = Gerenciador.getInstancia().getArvore();
        ArvoreModel treeModel = (ArvoreModel) arvore.getModel();
        return treeModel;
    }

    /** Adiciona um listener. */
    public void addListener(EventListener listener) {
        listenerList.add(EventListener.class, listener);
    }

    /** Remove um listener */
    public void removeListener(EventListener listener) {
        listenerList.remove(EventListener.class, listener);
    }

    public void addGradiatorListener(GradiadorListener listener) {
        listenerList.add(GradiadorListener.class, listener);
    }

    public void removeGradiatorListener(GradiadorListener listener) {
        listenerList.remove(GradiadorListener.class, listener);
    }

    protected void fireEvent(Enum<?> tipo) {
        Object[] listeners = listenerList.getListenerList();

        /* Each listener occupies two elements - the first is the listener class */
        /* and the second is the listener instance */
        for ( int i = 0; i < listeners.length; i += 2 ) {
            if ( listeners[i] == GradiadorListener.class ) {

                GradiadorListener listener = (GradiadorListener) listeners[i + 1];

                switch ( (Evento) tipo ) {
                    case REPLACED:
                        listener.replaced();
                        break;
                    default:
                }
            }
        }
    }

    @Override
    public void replaced() {
        Arvore arvore = Gerenciador.getInstancia().getArvore();

        /* se nao tem grade, entao deixa modelo vazio */
        if ( grade == null ) {
            arvore.setModel(new ArvoreModel());
            return;
        }

        /* prepara um novo modelo */
        ArvoreModel modelo = new ArvoreModel(grade);
        /* atualizando a arvore lateral */
        arvore.setModel(modelo);

    }

}


interface GradiadorListener extends EventListener {

    public void replaced();

}
